#include "MulticastReceiver.h"

#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#include <QThread>
#include <QDebug>

/**
 * @brief 构造函数
 * @param groupAddr 组播地址
 * @param groupPort 组播端口号
 * @param interface 指定的网卡接口
 * @param parent 父对象
 */
MulticastReceiver::MulticastReceiver(const std::string& groupAddr, u16 groupPort, const std::string& interface, QObject* parent)
    : QObject(parent)
    , sockfd_(-1)                // 套接字初始值为无效
    , groupAddr_(groupAddr)       // 组播地址
    , groupPort_(groupPort)       // 组播端口号
    , interface_(interface)       // 指定的网卡接口
{
}

/**
 * @brief 析构函数：清理资源
 */
MulticastReceiver::~MulticastReceiver()
{
    if (sockfd_ != -1)  // 如果套接字有效
    {
        leaveMulticastGroup(); // 离开组播组
        close(sockfd_);        // 关闭套接字
    }
}

/**
 * @brief 初始化套接字
 * @return 是否成功初始化
 */
bool MulticastReceiver::initSocket()
{
    sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);   // 创建UDP套接字
    if (sockfd_ < 0)        // 创建失败
    {
        perror("Socket creation failed");
        return false;
    }

    // 配置套接字绑定地址
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET; // 地址族为 IPv4
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 接收任何源IP的包
    addr.sin_port        = htons(groupPort_);   // 设置端口号

    // 绑定套接字到指定地址和端口
    if (bind(sockfd_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
    {
        perror("Bind failed");  // 绑定失败
        close(sockfd_);
        sockfd_ = -1;
        return false;
    }

    return true;
}

/**
 * @brief 加入组播组
 * @return 是否成功加入
 */
bool MulticastReceiver::joinMulticastGroup()
{
    struct ip_mreqn mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(groupAddr_.c_str());

    // 使用 if_nametoindex() 获取接口索引
    mreq.imr_ifindex = static_cast<int>(if_nametoindex(interface_.c_str()));
    if (mreq.imr_ifindex == 0)
    {
        std::cerr << "Error: Unable to find the interface " << interface_ << std::endl;
        return false;
    }

    if (setsockopt(sockfd_, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        perror("Setsockopt failed to join multicast group");
        return false;
    }

    return true;
}

/**
 * @brief 离开组播组
 * @return 是否成功离开
 */
bool MulticastReceiver::leaveMulticastGroup()
{
    struct ip_mreqn mreq;
    memset(&mreq, 0, sizeof(mreq));
    mreq.imr_multiaddr.s_addr = inet_addr(groupAddr_.c_str());

    // 使用 if_nametoindex() 获取网卡接口索引
    mreq.imr_ifindex = static_cast<int>(if_nametoindex(interface_.c_str()));
    if (mreq.imr_ifindex == 0)
    {
        std::cerr << "Error: Unable to find the interface " << interface_ << std::endl;
        return false;
    }

    if (setsockopt(sockfd_, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
    {
        perror("Setsockopt failed to leave multicast group");
        return false;
    }

    return true;
}

/**
 * @brief 数据接收线程的主函数
 */
void MulticastReceiver::processReceivedData()
{
    fd_set         readfds;     // 文件描述符集合
    struct timeval timeout;     // 超时时间
    s8             buffer[SIZE_LARGE];      // 接收缓冲区

    while (true)        // 无限循环接收数据
    {
        FD_ZERO(&readfds);      // 清空集合
        FD_SET(sockfd_, &readfds);   // 添加套接字到集合

        // 设置 2 秒的超时时间
        timeout.tv_sec  = 2;
        timeout.tv_usec = 0;

        // 检查是否有数据可读
        s32 ret = select(sockfd_ + 1, &readfds, NULL, NULL, &timeout);

        if (ret == -1)  // 错误
        {
            perror("Select error");
            break;
        }
        else if (ret == 0)  // 超时
        {
            // 超时，没有数据
            std::cout << "Timeout: No data received in the last 2 seconds." << std::endl;
            continue;
        }
        else    // 如果有数据
        {
            // 数据可读
            if (FD_ISSET(sockfd_, &readfds))
            {
                s32l len = recvfrom(sockfd_, buffer, sizeof(buffer), 0, NULL, NULL);    // 接收数据
                if (len < 0)
                {
                    perror("Receive failed");
                    break;
                }

                // 将接收到的数据转换为 std::string 并通过信号传递
                std::string receivedData(buffer, static_cast<size_t>(len));
                emit dataReceived(receivedData);
            }
        }
    }
}

/**
 * @brief 开始接收数据
 * @return 是否成功启动接收
 */
bool MulticastReceiver::startReceiving()
{
    if (!initSocket())      // 初始化套接字
    {
        return false;
    }

    if (!joinMulticastGroup())      // 加入组播组
    {
        return false;
    }

    // 创建一个 QThread 对象
    QThread* receiverThread = new QThread();

    // 将当前对象移至新的线程
    this->moveToThread(receiverThread);

    // 连接线程启动信号到 processReceivedData 槽
    connect(receiverThread, &QThread::started, this, &MulticastReceiver::processReceivedData);

    // 当 processReceivedData 完成后，退出线程
    connect(this, &MulticastReceiver::dataReceived, receiverThread, &QThread::quit);

    // 连接线程完成信号到线程删除槽
    connect(receiverThread, &QThread::finished, receiverThread, &QObject::deleteLater);

    // 启动线程
    receiverThread->start();

    return true;
}
